package com.animo.list.ext.impl;

import com.animo.list.ext.ExtList;

import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * @author 刘金泳
 * @Date 2020/7/28
 */
public class ExtArrayList<E> implements ExtList<E> {

    /**
     * 默认空数组
     */
    public static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存放元素的数组
     */
    transient Object[] elementData;

    /**
     * 数组中的元素个数
     */
    private int size = 0;

    /**
     * 默认初始化大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    private transient volatile int modCount = 0;

    /**
     * 构造方法不会去初始化数组大小，只有add的时候才会去初始化/扩容
     */
    public ExtArrayList(){
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean add(E e) {
        // 校验下一个存储位置是否足够
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    public void ensureCapacityInternal(int minCapacity){
        // 无参构造初始化之后第一次add会进入
        if(elementData  == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            // minCapacity = 10
            minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);
        }
        modCount++;
        // 如果下一个添加的位置大于当前数组长度 则进入扩容
        if(minCapacity - elementData.length > 0){
            // 扩容
            grow(minCapacity);
        }
    }


    private void grow(int minCapacity){
        // 获取当前元素的长度
        int oldCapacity = elementData.length;
        // 原长度基础上增加50% 1.5倍 相对于原数组的话是50%的说法
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新扩容的大小减去下一个存储位置小于0 则把下一个位置赋值给扩容长度 这个判断只有第一次添加数据的时候进入
        if(newCapacity - minCapacity < 0){
            newCapacity = minCapacity;
        }
        // 源码部分这里有一段判断当前扩容大小超过Integer大小的判断 这里不实现
        elementData = Arrays.copyOf(elementData,newCapacity);
    }

    @Override
    public E remove(int index) {
        modCount++;
        // 获取需要删除的元素
        E oldData = get(index);
        // 计算需要拷贝的长度
        int numMoved = size - index - 1;
        // 如果删除的下标不是最后一个元素就会进入
        if(numMoved > 0){
            /**
             * src 原数组
             * srcPos 原数组起始位置
             * dest 目标数组
             * destPos 目标数组起始位置
             * length 拷贝长度
             */
            /**
             * 假设int index = 0;
             * 原数组 new Object[]{'0',【‘1’】,'2','3'}; 前面两个参数
             * 目标数组 new Object[]{【‘0’】,'1','2','3'}; 后面两个参数
             *
             * 拷贝长度就是通过size元素个数 减去 index - 1
             * 这里的拷贝长度主要针对原数组所以是1的位置包括1往后推numMoved位就是要拷贝的数据
             *
             * 然后拷贝到目标数组的index位置也就是需要删除的位置
             *
             * 在把最后一个下标的数据 null
             */
            System.arraycopy(elementData,index + 1,elementData,index,numMoved);
        }
        elementData[--size] = null;
        return oldData;
    }
    @Override
    public E get(int index) {
        rangeCheck(index);
        return (E) elementData[index];
    }

    /**
     * 检测数组越界
     * @param index
     */
    private void rangeCheck(int index) {
        if (index >= size){
            throw new IndexOutOfBoundsException("数组越界啦！！:" + index);
        }
    }

    public static void main(String[] args) {
        ExtList<String> extList = new ExtArrayList<>();
        extList.add("0");
        extList.add("1");
        extList.add("2");
        extList.add("3");
        extList.remove(0);
    }

}
